﻿using System;
using Microsoft.Xna.Framework;

namespace GDD2_Game3
{
	struct FloatBox : IEquatable<FloatBox>
	{
		#region Emptiness
		public static FloatBox Empty { get; private set; }
		static FloatBox()
		{
			Empty = CreateGivenTopLeft(0, 0, 0, 0);
		}
		public bool IsEmpty()
		{
			return Dimensions.X * Dimensions.Y == 0.0f;
		}
		#endregion

		public Vector2 TopLeft { get; private set; }
		public Vector2 Center { get; private set; }
		public Vector2 Dimensions { get; private set; }
		public Rectangle Rectangle { get; private set; }
		public float LeftSideX { get; private set; }
		public float RightSideX { get; private set; }
		public float TopSideY { get; private set; }
		public float BottomSideY { get; private set; }
		public float Width
		{
			get { return Dimensions.X; }
		}
		public float Height
		{
			get { return Dimensions.Y; }
		}
		public override string ToString()
		{
			return string.Format("FloatBox: TopLeft {0}, Center {1}, Dimensions {2}, Rectangle {3}", TopLeft, Center, Dimensions, Rectangle);
		}

		#region Equality Stuff
		public override bool Equals(object obj)
		{
			return obj is FloatBox && Equals((FloatBox)obj);
		}
		public bool Equals(FloatBox other)
		{
			return TopLeft == other.TopLeft && Dimensions == other.Dimensions;
		}
		public override int GetHashCode()
		{
			return TopLeft.GetHashCode() ^ Dimensions.GetHashCode();
		}
		public static bool operator ==(FloatBox first, FloatBox second)
		{
			return first.Equals(second);
		}
		public static bool operator !=(FloatBox first, FloatBox second)
		{
			return !first.Equals(second);
		}
		#endregion

		#region Creation Methods
		#region Private Construction Helpers
		private static FloatBox CreateGivenPartial(FloatBox partial)
		{
            partial.Rectangle = new Rectangle(
				Convert.ToInt32(partial.TopLeft.X),
				Convert.ToInt32(partial.TopLeft.Y),
				Convert.ToInt32(partial.Dimensions.X),
				Convert.ToInt32(partial.Dimensions.Y));
			return partial;
		}
		
		private static FloatBox CreatePartialGivenBothPositions(Vector2 topLeft, Vector2 center, Vector2 dimensions)
		{
			FloatBox floatBox = new FloatBox();
			floatBox.TopLeft = topLeft;
			floatBox.Center = center;
			floatBox.Dimensions = dimensions;
			floatBox.LeftSideX = topLeft.X;
			floatBox.RightSideX = topLeft.X + dimensions.X;
			floatBox.TopSideY = topLeft.Y;
			floatBox.BottomSideY = topLeft.Y + dimensions.Y;
			return floatBox;
		}
		
		private static FloatBox CreatePartialGivenCenter(Vector2 center, Vector2 dimensions)
		{
			return CreatePartialGivenBothPositions(center - (dimensions / 2.0f), center, dimensions);
		}
		
		private static FloatBox CreatePartialGivenTopLeft(Vector2 topLeft, Vector2 dimensions)
		{
			return CreatePartialGivenBothPositions(topLeft, topLeft + (dimensions / 2.0f), dimensions);
		}
		#endregion
		public static FloatBox CreateGivenCenter(Vector2 center, Vector2 dimensions)
		{
			return CreateGivenPartial(CreatePartialGivenCenter(center, dimensions));
		}
		public static FloatBox CreateGivenCenter(float x, float y, float width, float height)
		{
			return CreateGivenCenter(new Vector2(x, y), new Vector2(width, height));
		}
		public static FloatBox CreateGivenTopLeft(Vector2 topLeft, Vector2 dimensions)
		{
			return CreateGivenPartial(CreatePartialGivenTopLeft(topLeft, dimensions));
		}
		public static FloatBox CreateGivenTopLeft(float x, float y, float width, float height)
		{
			return CreateGivenTopLeft(new Vector2(x, y), new Vector2(width, height));
		}
		public static FloatBox CreateFromRectangle(Rectangle rectangle)
		{
			FloatBox partial = CreatePartialGivenTopLeft(new Vector2(rectangle.X, rectangle.Y), new Vector2(rectangle.Width, rectangle.Height));
			partial.Rectangle = rectangle;
			return partial;
		}
		#endregion

		public FloatBox ShiftedBy(Vector2 offset)
		{
			return CreateGivenTopLeft(TopLeft + offset, Dimensions);
		}
		public static void Shift(ref FloatBox initial, Vector2 offset)
		{
			initial = initial.ShiftedBy(offset);
		}

		public static FloatBox AbsoluteIntersection(FloatBox first, FloatBox second)
		{
			if(first.IsEmpty() || second.IsEmpty())
			{
				return Empty;
			}
			Vector2 topLeft = new Vector2(Math.Max(first.LeftSideX, second.LeftSideX), Math.Max(first.TopSideY, second.TopSideY));
			Vector2 bottomRight = new Vector2(Math.Min(first.RightSideX, second.RightSideX), Math.Min(first.BottomSideY, second.BottomSideY));
			Vector2 dimensions = bottomRight - topLeft;
			if(dimensions.X > 0.0f && dimensions.Y > 0.0f)
			{
				return CreateGivenTopLeft(topLeft, dimensions);
			}
			else
			{
				return Empty;
			}
		}

		public FloatBox RelativeIntersection(FloatBox other)
		{
			FloatBox absoluteIntersection = AbsoluteIntersection(this, other);
			if(absoluteIntersection.IsEmpty())
			{
				return Empty;
			}
			else
			{
				return CreateGivenTopLeft(absoluteIntersection.TopLeft - TopLeft, absoluteIntersection.Dimensions);
			}
		}

		public bool Intersects(FloatBox other)
		{
			return !AbsoluteIntersection(this, other).IsEmpty();
		}

		public static FloatBox Union(FloatBox first, FloatBox second)
		{
			if(first.IsEmpty())
			{
				return second;
			}
			if(second.IsEmpty())
			{
				return first;
			}
			Vector2 topLeft = new Vector2(Math.Min(first.LeftSideX, second.LeftSideX), Math.Min(first.TopSideY, second.TopSideY));
			Vector2 bottomRight = new Vector2(Math.Max(first.RightSideX, second.RightSideX), Math.Max(first.BottomSideY, second.BottomSideY));
			Vector2 dimensions = bottomRight - topLeft;
			if(dimensions.X > 0.0f && dimensions.Y > 0.0f)
			{
				return CreateGivenTopLeft(topLeft, dimensions);
			}
			else
			{
				return Empty;
			}
		}

		public FloatBox ScaledBy(Vector2 scaleFactor)
		{
			return FloatBox.CreateGivenTopLeft(TopLeft * scaleFactor, Dimensions * scaleFactor);
		}

		public FloatBox ScaledBy(float scaleFactor)
		{
			return FloatBox.CreateGivenTopLeft(TopLeft * scaleFactor, Dimensions * scaleFactor);
		}
	}
}
